package edu.stanford.classhierarchy;

import java.io.*;
import java.util.*;
import acm.program.*;
import acm.graphics.*;

public class ClassHierarchy extends GraphicsProgram
{
    private final String FILE_LOCATION;
    private ObjectList objects;
    private static final double CLASSBOX_HEIGHT = 40.00;
    private static final double CLASSBOX_WIDTH = 72.00;
    private static final double DISTANCE_BTW_NODES = 72.00;
    private static double WINDOW_WIDTH;
    private static double WINDOW_HEIGHT;

    public ClassHierarchy()
    {
	FILE_LOCATION = "";
    }

    public ClassHierarchy(String aLocation) throws IOException, FileNotFoundException
    {
	FILE_LOCATION = aLocation;
	objects = new ObjectList();
	readFile();
    }
    
    private void readFile() throws IOException, FileNotFoundException
    {
	/*
	 * only accepts a single file as source
	 * future version may include option for directory-layout as source 
	 */
	String currentLine = null;
	BufferedReader reader = null;

	try
	{
	    reader = new BufferedReader(new FileReader(FILE_LOCATION));
	    String[] bits;
	    while((currentLine = reader.readLine()) != null)
	    {
		bits = currentLine.split(":");
		if (bits.length != 2) continue;
		if (bits[0] == "" && bits[1] =="") continue;
		if ((bits[0] == "NULL" || bits[0] == "null" || bits[0] == "") && bits[1] != "") objects.put(null, bits[1]);
		if (bits[1] == "" && bits[0] != "") objects.put(null, bits[0]);
		if (bits[0] != "" && bits[1] != "") objects.put(bits[0], bits[1]);
	    }
	}
	catch(FileNotFoundException e)
	{
	    System.out.println("Error... file not found!! Check if filename and  path are correct");
	    e.printStackTrace();
	    System.exit(1);
	}
	catch (IOException e)
        {
	    System.out.println("Error... reading file!! check File-Permissions or if the file is not empty or corrupted...");
	    e.printStackTrace();
	    System.exit(1);
	}
	if(reader != null) reader.close();
    }

    private void createClassGraphic(double position_x, double position_y, double aWidth, double aHeight, String alabel)
    {
	GRect box = new GRect(position_x, position_y, aWidth, aHeight);
	GLabel label = new GLabel(alabel);
	label.move((position_x + (aWidth - label.getWidth())/2.00), (position_y + (aHeight + label.getAscent())/2.00));
	add(box);
	add(label);
    }

    private void createLine(double ax1, double ay1, double ax2, double ay2)
    {
	GLine line = new GLine(ax1, ay1, ax2, ay2);
	add(line);
    }

    public void init()
    {
	WINDOW_WIDTH = (double) getWidth();
	WINDOW_HEIGHT = (double) getHeight();

	Set set = objects.entrySet();
	Iterator parent_iterator = set.iterator();
	int parent_count = objects.size();
	double line_x1, line_y1, line_x2, line_y2;

	for(int i = 0; i < parent_count; ++i)
	{
	    Map.Entry parent = (Map.Entry) parent_iterator.next();
	    String parent_label = (String) parent.getKey();

	    @SuppressWarnings("unchecked")
	    ArrayList<String> children =  (ArrayList<String>) parent.getValue();
	    int children_count = children.size();

	    double vertical_displacement = WINDOW_HEIGHT * (2 * (i + 1) - 1)/(2.00 * parent_count);
	    double horizontal_displacement = WINDOW_WIDTH * (1 + children_count) / (2.00 * children_count);
	    line_x2 = horizontal_displacement + CLASSBOX_WIDTH / 2.00;
	    line_y2 = vertical_displacement + CLASSBOX_HEIGHT;

	    createClassGraphic(horizontal_displacement, vertical_displacement, CLASSBOX_WIDTH, CLASSBOX_HEIGHT, parent_label);
	    vertical_displacement = vertical_displacement + DISTANCE_BTW_NODES;
	    line_y1 = vertical_displacement;

	    for(int j = 0; j < children_count; ++j)
	    {
		String child_label = children.get(j);
		horizontal_displacement = WINDOW_WIDTH * (j + 1)/children_count;
		line_x1 = horizontal_displacement + CLASSBOX_WIDTH / 2.00;

	        createClassGraphic(horizontal_displacement, vertical_displacement, CLASSBOX_WIDTH, CLASSBOX_HEIGHT, child_label);
		createLine(line_x1, line_y1, line_x2, line_y2);
	    }
	}
    }

    public static void main(String args[])
    {
	String file_location = null;
	ClassHierarchy visualHierarchy;

	Scanner in = new Scanner(System.in);
	System.out.print("Provide location of the file containing Class Hierarchy >> ");

	file_location = in.nextLine();

	if (file_location != "")
	{
	    try
	    {
	       visualHierarchy = new ClassHierarchy(file_location);
	       visualHierarchy.start();
	    }
	    catch (Exception e)
	    {
		e.printStackTrace();
		System.exit(1);
	    }
        }
    }
}

class ObjectList extends HashMap<String, ArrayList<String>>
{
    public void put(String key, String value)
    {
	ArrayList<String> current = get(key);
	if (current == null)
	    {
		current = new ArrayList<String>();
		super.put(key, current);
	    }
	current.add(value);
    }
}
